
#include <BStyle>
#include <BPainter>
#include "member_BGraphicItem.h"
#include "member_BGraphicWidget.h"
#include "member_BStyle.h"
#include "member_BObject.h"

using namespace BWE;

member_BGraphicItem::member_BGraphicItem(BGraphicItem* graphicItem)
{
	boss = graphicItem;
	graphic = 0;
	parent = 0;
	visible = true;
	refresh = true;
	dirty = true;
	focusable = true;
	focused = false;
	selected = false;
	hovered = false;
	pressed = false;
	dirtyStyle = true;
	rotate = 0;
	border = 1;
	size.set(100, 100);
	originAlign = Align_None;
}
member_BGraphicItem::~member_BGraphicItem()
{

}

void member_BGraphicItem::raiseDirty()
{
	BGraphicItem* item = boss;
	while (item)
	{
		item_member(item)->dirty = true;
		item->emit(Signal_Dirty);
		item = item_member(item)->parent;
	}
	if (graphic)
	{
		graphic_member(graphic)->emit(Signal_Dirty);
	}
}
void member_BGraphicItem::raiseSignal(int signal, const BValue& value)
{
	BGraphicItem* item = boss;
	while (item)
	{
		item->emit(signal, value);
		item = item_member(item)->parent;
	}
	if (graphic)
	{
		graphic_member(graphic)->emit(signal, value);
	}
}

void member_BGraphicItem::attach(BGraphicWidget* graphic)
{
	if (this->graphic == graphic)
		return;
	this->graphic = graphic;
	if (graphic)
	{
		dirty = true;
		selected = false;
		if (widget)
			graphic->addChild(widget);
		for (int i = 0; i < children.size(); i++)
		{
			BGraphicItem* item = children[i];
			item_member(item)->attach(graphic);
		}
	}
}
void member_BGraphicItem::detach(BGraphicWidget* graphic)
{
	if (this->graphic != graphic)
		return;
	if (graphic)
	{
		if (widget)
			graphic->removeChild(widget);
		for (int i = 0; i < children.size(); i++)
		{
			BGraphicItem* item = children[i];
			item_member(item)->detach(graphic);
		}
		if (graphic_member(graphic)->selectedItems.contain(boss))
		{
			selected = false;
			graphic_member(graphic)->selectedItems.remove(boss);
			object_member(graphic)->emit(Signal_ItemSelected, boss);
		}
		graphic_member(graphic)->visualItems.remove(boss);
		if (graphic_member(graphic)->hoveredItem == boss)
		{
			graphic_member(graphic)->hoveredItem = 0;
			object_member(graphic)->emit(Signal_ItemHovered, 0);
		}
	}
	this->graphic = 0;
}

void member_BGraphicItem::dispenseUpperStyles(BGraphicItem* upper)
{
	if (BGraphicItem* parent = item_member(upper)->parent)
	{
		dispenseUpperStyles(parent);
	}
	if (BStyle* style = item_member(upper)->realStyle)
	{
		style_member(style)->apply(boss);
	}
}
void member_BGraphicItem::dispenseStyles(BEvent& event)
{
	if (realStyle)
	{
		realStyle->clearAll();
		realStyle->clearAnnexes();
		realStyle->clearChildren();
	}
	style_member(graphic->realStyle())->apply(boss);
	if (parent)
	{
		dispenseUpperStyles(parent);
	}
	for (int i = 0; i < children.size(); i++)
	{
		BGraphicItem* child = children[i];
		item_member(child)->dispenseStyles(event);
		item_member(child)->dirtyStyle = false;
	}
	event.setValue(boss->style());
	this->event(event);
}
void member_BGraphicItem::applyStyle()
{
	if (BStyle* style = realStyle)
	{
		const BValue& borderValue = style_member(style)->valueMap(Value_Border);
		if (borderValue.valid())
			border = (float)(int)borderValue;

		const BValue& topValue = style_member(style)->valueMap(Value_Margin_Top);
		if (topValue.valid())
			margin.top() = (float)(int)topValue;

		const BValue& leftValue = style_member(style)->valueMap(Value_Margin_Left);
		if (leftValue.valid())
			margin.left() = (float)(int)leftValue;

		const BValue& rightValue = style_member(style)->valueMap(Value_Margin_Right);
		if (rightValue.valid())
			margin.right() = (float)(int)rightValue;

		const BValue& bottomValue = style_member(style)->valueMap(Value_Margin_Bottom);
		if (bottomValue.valid())
			margin.bottom() = (float)(int)bottomValue;

		const BValue& tipsValue = style->value(Value_Tips);
		if (tipsValue.valid())
		{
			this->tips = (const BString&)tipsValue;
			boss->emit(Signal_TipsChanged, tipsValue);
		}
		dirty = true;
	}
}
void member_BGraphicItem::applyRoundStyle()
{
	BSize radiusLeftTop;
	BSize radiusRightTop;
	BSize radiusLeftBottom;
	BSize radiusRightBottom;
	if (round)
	{
		radiusLeftTop = round->radiusLeftTop;
		radiusRightTop = round->radiusRightTop;
		radiusLeftBottom = round->radiusLeftBottom;
		radiusRightBottom = round->radiusRightBottom;
	}

	if (BStyle* style = realStyle)
	{
		style_member(style)->assign(Value_Radius_Left_Top, radiusLeftTop);
		style_member(style)->assign(Value_Radius_Right_Top, radiusRightTop);
		style_member(style)->assign(Value_Radius_Left_Bottom, radiusLeftBottom);
		style_member(style)->assign(Value_Radius_Right_Bottom, radiusRightBottom);
	}

	if (radiusLeftTop.empty() && radiusRightTop.empty() && radiusLeftBottom.empty() && radiusRightBottom.empty())
	{
		if (round)
		{
			round = 0;
			dirty = true;
		}
	}
	else
	{
		if (round.empty())
			round = new IRound();
		round->radiusLeftTop = radiusLeftTop;
		round->radiusRightTop = radiusRightTop;
		round->radiusLeftBottom = radiusLeftBottom;
		round->radiusRightBottom = radiusRightBottom;
		dirty = true;
	}
}

void member_BGraphicItem::dirtyCell()
{
	if (graphic)
	{
		dirty = true;
		graphic_member(graphic)->dirty = true;
		graphic->fresh();
	}
}
void member_BGraphicItem::freshRects()
{
	float b = border;
	float width = rect.width();
	float height = rect.height();

	float pleft = perch.left();
	float pright = perch.right();
	float ptop = perch.top();
	float pbottom = perch.bottom();

	float mleft = margin.left();
	float mright = margin.right();
	float mtop = margin.top();
	float mbottom = margin.bottom();

	float cx = b + pleft + mleft;
	float cy = b + ptop + mtop;
	float cw = width - b - pleft - mleft - mright - pright - b;
	float ch = height - b - ptop - mtop - mbottom - pbottom - b;

	rectTitle.set(b, b, width - b - b, ptop);
	rectTop.set(b + pleft, b, width - b - pleft - b - pright, ptop);
	rectRight.set(width - b - pright, b + ptop, pright, height - b - b - ptop - pbottom);
	rectLeft.set(b, b + ptop, pleft, height - b - b - ptop - pbottom);
	rectBottom.set(b + pleft, height - b - pbottom, width - b - b - pleft - pright, pbottom);
	rectLeftTop.set(b, b, pleft, ptop);
	rectLeftBottom.set(b, height - b - pbottom, pleft, pbottom);
	rectRightTop.set(width - b - pright, b, pright, ptop);
	rectRightBottom.set(width - b - pright, height - b - pbottom, pright, pbottom);
	rectCenter.set(cx, cy, cw, ch);

	if (round)
	{
		round->frameSize.set((float)width, (float)height);
		round->centerLeftTop.set(round->radiusLeftTop.width(), round->radiusLeftTop.height());
		round->centerRightTop.set(width - round->radiusRightTop.width(), round->radiusRightTop.height());
		round->centerLeftBottom.set(round->radiusLeftBottom.width(), height - round->radiusLeftBottom.height());
		round->centerRightBottom.set(width - round->radiusRightBottom.width(), height - round->radiusRightBottom.height());

		BPointfArray outerPoints;
		BPointfArray innerPoints;

		BRectf outerRect(0, 0, width, height);
		round->createPoints(outerRect, outerPoints);

		BRectf innerRect = outerRect;
		innerRect.left() += border;
		innerRect.right() -= border;
		innerRect.top() += border;
		innerRect.bottom() -= border;

		round->createPoints(innerRect, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->borderPoints);

		round->backgroundPoints = innerPoints;
		round->backgroundPoints.prepend(rectCenter.center());

		outerPoints.reset();
		round->createLeftTopPoints(innerRect, outerPoints);
		round->createRightTopPoints(innerRect, outerPoints);
		outerPoints.prepend(BPointf((float)rectLeft.right(), (float)rectTop.bottom()));
		outerPoints.append(BPointf((float)rectRight.left(), (float)rectTop.bottom()));
		outerPoints.append(BPointf((float)rectLeft.right(), (float)rectTop.bottom()));
		round->createTriangles(outerPoints, rectTop.center(), round->titlePoints);

		innerRect.left() = (float)rectLeft.right();
		innerRect.top() = (float)rectTop.bottom();
		innerRect.right() = (float)rectRight.left();
		innerRect.bottom() = (float)rectBottom.top();

		outerPoints = innerPoints;
		if (innerRect.valid())
		{
			round->createPoints(innerRect, innerPoints);
			round->createTriangles(outerPoints, innerPoints, round->perchPoints);
		}
		else
		{
			round->createTriangles(outerPoints, rectTop.center(), round->perchPoints);
			round->createTriangles(outerPoints, rectTop.center(), round->titlePoints);
		}

		outerPoints = innerPoints;
		round->createPoints(rectCenter, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->marginPoints);

		round->centerPoints = innerPoints;
		round->centerPoints.prepend(rectCenter.center());

		round->movePoints(rectBack.pos());
	}

	rectTitle += rectBack.pos();
	rectCenter += rectBack.pos();
	
	rectTop += rectBack.pos();
	rectLeft += rectBack.pos();
	rectRight += rectBack.pos();
	rectBottom += rectBack.pos();
	
	rectLeftTop += rectBack.pos();
	rectLeftBottom += rectBack.pos();
	rectRightTop += rectBack.pos();
	rectRightBottom += rectBack.pos();
}
void member_BGraphicItem::adjustRect(const BPointf& offset)
{
	if (widget)
	{
		BPointf scale = graphic->areaScale();
		BRect crect = rectCenter + boss->absolutePos();
		crect.scale(scale.x(), scale.y());
		widget->setRect(crect + offset);
	}
}

void member_BGraphicItem::paint(BPainter& painter)
{
	painter.setStyle(realStyle);
	painter.pushAttrib();
	boss->paint(painter);
	painter.popAttrib();
}
void member_BGraphicItem::event(const BEvent& event)
{
	Event type = event.type();
	if (type == Event_Style)
	{
		if (realStyle)
		{
			applyStyle();
			applyRoundStyle();
		}
		if (dirty)
		{
			freshRects();
			BEvent resizeEvent(Event_Resize);
			resizeEvent.setValue(rect.size());
			this->event(resizeEvent);
		}
		boss->fresh();
	}
	else if (type == Event_Fresh)
	{
		if (dirtyStyle)
		{
			BEvent styleEvent(Event_Style);
			dispenseStyles(styleEvent);
			dirtyStyle = false;
		}
		if (widget)
		{
			widget->setRect(rectCenter);
		}
	}
	else if (type == Event_Resize)
	{
		boss->fresh();
	}

	switch (event.type())
	{
	case Event_Update:
		boss->updateEvent(event);
		break;
	case Event_Style:
		boss->styleEvent(event);
		break;
	case Event_Fresh:
		boss->freshEvent(event);
		break;
	case Event_Focus:
		boss->focusEvent(event);
		break;
	case Event_Move:
		boss->moveEvent(event);
		break;
	case Event_Rotate:
		boss->rotateEvent(event);
		break;
	case Event_Resize:
		boss->resizeEvent(event);
		break;
	case Event_KeyPress:
		boss->keyPressEvent((const BKeyEvent&)event);
		break;
	case Event_KeyRelease:
		boss->keyReleaseEvent((const BKeyEvent&)event);
		break;
	case Event_MouseEnter:
		boss->mouseEnterEvent((const BMouseEvent&)event);
		break;
	case Event_MouseLeave:
		boss->mouseLeaveEvent((const BMouseEvent&)event);
		break;
	case Event_MousePress:
		boss->mousePressEvent((const BMouseEvent&)event);
		break;
	case Event_MouseRelease:
		boss->mouseReleaseEvent((const BMouseEvent&)event);
		break;
	case Event_MouseMove:
		boss->mouseMoveEvent((const BMouseEvent&)event);
		break;
	case Event_MouseWheel:
		boss->mouseWheelEvent((const BMouseEvent&)event);
		break;
	case Event_MouseClick:
		boss->mouseClickEvent((const BMouseEvent&)event);
		break;
	case Event_DoubleClick:
		boss->mouseDoubleClickEvent((const BMouseEvent&)event);
		break;
	case Event_DragEnter:
		boss->dragEnterEvent((const BMouseEvent&)event);
		break;
	case Event_DragLeave:
		boss->dragLeaveEvent((const BMouseEvent&)event);
		break;
	case Event_DragMove:
		boss->dragMoveEvent((const BMouseEvent&)event);
		break;
	case Event_DragDrop:
		boss->dragDropEvent((const BMouseEvent&)event);
		break;
	default:
		break;
	}
}
